package com.storage.framework.shiro.web.session;

import com.storage.common.constant.ShiroConstants;
import com.storage.common.utils.StringUtils;
import com.storage.common.utils.spring.SpringUtils;
import com.storage.framework.shiro.session.OnlineSession;
import com.storage.system.domain.SysUserOnline;
import com.storage.system.service.ISysUserOnlineService;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.shiro.session.ExpiredSessionException;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.servlet.ShiroHttpSession;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
 * 主要是在此如果会话的属性修改了 就标识下其修改了 然后方便 OnlineSessionDao同步
 *
 * @author hzhang7
 */
public class OnlineWebSessionManager extends DefaultWebSessionManager {

    private static final Logger logger = LoggerFactory.getLogger(OnlineWebSessionManager.class);

    @Override
    public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException {
        super.setAttribute(sessionKey, attributeKey, value);
        if (value != null && needMarkAttributeChanged(attributeKey)) {
            OnlineSession s = (OnlineSession) doGetSession(sessionKey);
            s.markAttributeChanged();
        }
    }

    @Override
    public Object removeAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException {
        Object removed = super.removeAttribute(sessionKey, attributeKey);
        if (removed != null) {
            OnlineSession s = (OnlineSession) doGetSession(sessionKey);
            s.markAttributeChanged();
        }

        return removed;
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        Serializable id = super.getSessionId(request, response);
        if (id == null) {
            id = getReferencedSessionId(request, response);
            logger.error(">>>>>>>>>>从header中获取 jsessionid = {}", id);
        }
        return id;
    }

    @Override
    protected Collection<Session> getActiveSessions() {
        throw new UnsupportedOperationException("getActiveSessions method not supported");
    }

    /**
     * 验证session是否有效 用于删除过期session
     */
    @Override
    public void validateSessions() {
        super.validateSessions();
//        if (logger.isInfoEnabled()) {
//            logger.info("invalidation sessions...");
//        }
//
//        int invalidCount = 0;
//
//        int timeout = (int) this.getGlobalSessionTimeout();
//        Date expiredDate = DateUtils.addMilliseconds(new Date(), 0 - timeout);
//        ISysUserOnlineService userOnlineService = SpringUtils.getBean(ISysUserOnlineService.class);
//        List<SysUserOnline> userOnlineList = userOnlineService.selectOnlineByExpired(expiredDate);
//        // 批量过期删除
//        List<String> needOfflineIdList = new ArrayList<>();
//        for (SysUserOnline userOnline : userOnlineList) {
//            try {
//                SessionKey key = new DefaultSessionKey(userOnline.getSessionId());
//                Session session = retrieveSession(key);
//                if (session != null) {
//                    throw new InvalidSessionException();
//                }
//            } catch (InvalidSessionException e) {
//                if (logger.isDebugEnabled()) {
//                    boolean expired = (e instanceof ExpiredSessionException);
//                    String msg = "Invalidated session with id [" + userOnline.getSessionId() + "]" + (expired ? " (expired)" : " (stopped)");
//                    logger.debug(msg);
//                }
//                invalidCount++;
//                needOfflineIdList.add(userOnline.getSessionId());
//            }
//
//        }
//        if (needOfflineIdList.size() > 0) {
//            try {
//                userOnlineService.batchDeleteOnline(needOfflineIdList);
//            } catch (Exception e) {
//                logger.error("batch delete db session error.", e);
//            }
//        }
//
//        if (logger.isInfoEnabled()) {
//            String msg = "Finished invalidation session.";
//            if (invalidCount > 0) {
//                msg += " [" + invalidCount + "] sessions were stopped.";
//            } else {
//                msg += " No sessions were stopped.";
//            }
//            logger.info(msg);
//        }

    }

    /**
     * 获取sessionid
     *
     * @param request
     * @param response
     * @return
     */
    private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
        String id = this.getSessionIdHeaderValue(request, response);

        //DefaultWebSessionManager 中代码 直接copy过来
        if (id != null) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
        }
        //不会把sessionid放在URL后
        request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, Boolean.FALSE);
        return id;
    }

    /**
     * 请求头中获取 sessionId 并把sessionId 放入 response 中
     *
     * @param request
     * @param response
     * @return
     */
    private String getSessionIdHeaderValue(ServletRequest request, ServletResponse response) {
        if (!(request instanceof HttpServletRequest)) {
            logger.error("Current request is not an HttpServletRequest - cannot get session ID cookie.  Returning null.");
            return null;
        } else {
            HttpServletRequest httpRequest = (HttpServletRequest) request;

            // 在request 中 读取 x-auth-token 信息  作为 sessionId
            String sessionId = httpRequest.getHeader(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);

            // 每次读取之后 都把当前的 sessionId 放入 response 中
            HttpServletResponse httpResponse = (HttpServletResponse) response;

            if (StringUtils.isNotEmpty(sessionId)) {
                httpResponse.setHeader(ShiroHttpSession.DEFAULT_SESSION_ID_NAME, sessionId);
                logger.error("Current session ID is {}", sessionId);
            }

            return sessionId;
        }
    }

    /**
     * 标记属性更改
     *
     * @param attributeKey
     * @return
     */
    private boolean needMarkAttributeChanged(Object attributeKey) {
        if (attributeKey == null) {
            return false;
        }
        String attributeKeyStr = attributeKey.toString();
        // 优化 flash 属性没必要持久化
        if (attributeKeyStr.startsWith("org.springframework")) {
            return false;
        }
        if (attributeKeyStr.startsWith("javax.servlet")) {
            return false;
        }
        if (attributeKeyStr.equals(ShiroConstants.CURRENT_USERNAME)) {
            return false;
        }
        return true;
    }

}
